use super::*;
use lexer::*;

// ## 4. Specifying features

// ### 4.a. feature
#[derive(Debug, Clone)]
pub struct Feature {
	pub tag: Tag,
	pub base: std::path::PathBuf,
	pub lookups: Vec<FeatureItem>,
}
impl Feature {
	pub fn parse(r: &mut Lexer<'_>, base: std::path::PathBuf) -> Result<Self, Error> {
		let name = r.expect_ident()?;
		let mut this = Self {
			tag: Tag::try_from(name)?,
			base,
			lookups: vec![],
		};
		r.expect(Event::LBrace)?;
		let mut ctx = FeatureCtx::default();
		ctx.dirty = true;
		this.parse_impl(r, &mut ctx)?;
		r.expect(Event::Ident(name))?;
		r.expect(Event::Semi)?;
		Ok(this)
	}
	pub fn parse_impl(&mut self, r: &mut Lexer<'_>, ctx: &mut FeatureCtx) -> Result<(), Error> {
		while let Ok(token) = r.read_next() {
			match token {
				Event::RBrace => break,
				Event::IncludeKw => {
					r.expect(Event::LParen)?;
					let path = r.read_file()?;
					r.expect(Event::RParen)?;
					r.expect(Event::Semi)?;
					let path = self.base.join(path);
					let content = std::fs::read_to_string(path).map_err(|_| Error::Msg("inlucde not found"))?;
					self.parse_impl(&mut Lexer::new(&content), ctx)?;
				}
				Event::SubKw => {
					self.sure_lookup(ctx);
					self.push_sub(Substitution::parse(r)?)?
				}
				Event::RsubKw => {
					self.sure_lookup(ctx);
					self.push_sub(Substitution::parse_reverse(r)?)?
				}
				Event::IgnoreKw => match r.read_next()? {
					Event::SubKw => {
						self.sure_lookup(ctx);
						for sub in Substitution::parse_ignore(r)? {
							self.push_sub(sub)?;
						}
					}
					Event::PosKw => {
						self.sure_lookup(ctx);
						for sub in Position::parse_ignore(r)? {
							self.push_pos(sub)?;
						}
					}
					e => return Err(Error::Unexpect(format!("{:?}", e))),
				},
				Event::PosKw => {
					self.sure_lookup(ctx);
					self.push_pos(Position::parse(r)?)?
				}
				Event::EnumKw => match r.read_next()? {
					Event::PosKw => {
						self.sure_lookup(ctx);
						self.push_pos(Position::parse_enum(r)?)?
					}
					e => return Err(Error::Unexpect(format!("{:?}", e))),
				},
				Event::SubtableKw => {
					r.expect(Event::Semi)?;
					self.sure_lookup(ctx);
					let lookup = self.lookups.last_mut().ok_or(Error::Msg("lookup not exist"))?;
					lookup.lookup.rules.push(Rule::SubTable);
				}
				Event::LookupflagKw => ctx.flag = LookupFlag::parse(r)?,
				Event::LookupKw => {
					let name = r.expect_ident()?;
					let next = r.read_next()?;
					if next == Event::Semi {
						self.lookups.push(FeatureItem {
							script: ctx.script.clone(),
							language: ctx.language.clone(),
							lookup: Lookup::new("", self.base.clone(), false, ctx.flag.clone()),
							lookup_ref: Some(LookupRef(name.into())),
						});
					} else {
						let v = match next {
							Event::UseExtensionKw => {
								r.expect(Event::LBrace)?;
								Lookup::parse(r, name, self.base.clone(), true, ctx.flag.clone())
							}
							Event::LBrace => Lookup::parse(r, name, self.base.clone(), false, ctx.flag.clone()),
							e => Err(Error::Unexpect(format!("{:?}", e))),
						}?;
						self.lookups.push(FeatureItem {
							script: ctx.script.clone(),
							language: ctx.language.clone(),
							lookup: v,
							lookup_ref: None,
						});
					}
					ctx.dirty = true;
				}
				Event::LanguageKw => {
					// language <language tag> [exclude_dflt|include_dflt] [required];
					let name = r.expect_ident()?;
					let mut exclude = false;
					let mut required = false;
					loop {
						match r.read_next()? {
							Event::Semi => break,
							Event::ExcludeDfltKw => exclude = true,
							Event::IncludeDfltKw => exclude = false,
							Event::RequiredKw => required = true,
							e => return Err(Error::Unexpect(format!("{:?}", e))),
						}
					}
					ctx.language = Some(Language {
						name: name.into(),
						exclude,
						required,
					});
					ctx.flag = LookupFlag::default();
					ctx.dirty = true;
				}
				Event::ScriptKw => {
					// script <script tag>;
					let name = r.expect_ident()?;
					r.expect(Event::Semi)?;
					ctx.script = Some(Script(name.into()));
					ctx.language = None;
					ctx.flag = LookupFlag::default();
					ctx.dirty = true;
				}
				Event::ParametersKw if self.tag == Tag::SIZE || self.tag.is_character_variant() || self.tag.is_stylistic_set() => {
					// TODO
					loop {
						match r.read_next()? {
							Event::Semi => break,
							_ => {}
						}
					}
				}
				Event::FeatureKw if self.tag == Tag::AALT => {
					// TODO
					_ = r.expect_ident()?;
					r.expect(Event::Semi)?;
				}
				e => return Err(Error::Unexpect(format!("{:?}", e))),
			}
		}
		Ok(())
	}
	pub fn sure_lookup(&mut self, ctx: &mut FeatureCtx) {
		if ctx.dirty {
			self.lookups.push(FeatureItem {
				script: ctx.script.clone(),
				language: ctx.language.clone(),
				lookup: Lookup::new("", self.base.clone(), false, ctx.flag.clone()),
				lookup_ref: None,
			});
			ctx.dirty = false;
		}
	}
	pub fn push_sub(&mut self, sub: Substitution) -> Result<(), Error> {
		let lookup = self.lookups.last_mut().ok_or(Error::Msg("lookup not exist"))?;
		lookup.lookup.rules.push(Rule::Sub(sub));
		Ok(())
	}
	pub fn push_pos(&mut self, pos: Position) -> Result<(), Error> {
		let lookup = self.lookups.last_mut().ok_or(Error::Msg("lookup not exist"))?;
		lookup.lookup.rules.push(Rule::Pos(pos));
		Ok(())
	}
}
#[derive(Debug, Clone)]
pub struct FeatureItem {
	pub script: Option<Script>,
	pub language: Option<Language>,
	pub lookup: Lookup,
	pub lookup_ref: Option<LookupRef>,
}
#[derive(Debug, Default)]
pub struct FeatureCtx {
	pub script: Option<Script>,
	pub language: Option<Language>,
	pub flag: LookupFlag,
	pub dirty: bool,
}

// ### 4.b. Language system

// #### 4.b.i. languagesystem
#[derive(Debug, Clone)]
pub struct LanguageSystem(pub Script, pub Language);
impl LanguageSystem {
	pub fn parse(r: &mut Lexer<'_>) -> Result<Self, Error> {
		let scipt = r.expect_ident()?;
		let language = r.expect_ident()?;
		r.expect(Event::Semi)?;
		Ok(Self(Script(scipt.into()), Language::new(language)))
	}
}
// #### 4.b.ii. script and language
#[derive(Debug, Clone)]
pub struct Script(pub String);

#[derive(Debug, Clone)]
pub struct Language {
	pub name: String,
	pub exclude: bool,
	pub required: bool,
}
impl Language {
	pub fn new(name: &str) -> Self {
		Self {
			name: name.into(),
			exclude: false,
			required: false,
		}
	}
}

// ### 4.c. parameters

// ### 4.d. lookupflag
#[derive(Debug, Clone, Default)]
pub struct LookupFlag(pub u16, pub Option<Class>);
impl LookupFlag {
	pub const RIGHT_TO_LEFT: Self = Self(0x0001, None);
	pub const IGNORE_BASE_GLYPHS: Self = Self(0x0002, None);
	pub const IGNORE_LIGATURES: Self = Self(0x0004, None);
	pub const IGNORE_MARKS: Self = Self(0x0008, None);
	pub const USE_MARK_FILTERING_SET: Self = Self(0x0010, None);
	pub const MARK_ATTACHMENT_CLASS_FILTER: Self = Self(0xFF00, None);
	pub fn parse(r: &mut Lexer<'_>) -> Result<Self, Error> {
		let mut this = Self::default();
		loop {
			match r.read_next()? {
				Event::Semi => break,
				Event::RightToLeftKw => this.0 |= Self::RIGHT_TO_LEFT.0,           // format A
				Event::IgnoreBaseGlyphsKw => this.0 |= Self::IGNORE_BASE_GLYPHS.0, // format A
				Event::IgnoreLigaturesKw => this.0 |= Self::IGNORE_LIGATURES.0,    // format A
				Event::IgnoreMarksKw => this.0 |= Self::IGNORE_MARKS.0,            // format A
				Event::UseMarkFilteringSetKw => match r.read_next()? {
					Event::LSquare => {
						// format A
						this.0 |= Self::USE_MARK_FILTERING_SET.0;
						this.1 = Some(Class::parse(r)?);
					}
					Event::Group(v) => {
						// format A
						this.0 |= Self::USE_MARK_FILTERING_SET.0;
						this.1 = Some(Class(vec![ClassItem::Ref(v.into())]));
					}
					e => return Err(Error::Unexpect(format!("{:?}", e))),
				},
				Event::MarkAttachmentTypeKw => match r.read_next()? {
					Event::LSquare => {
						// format A
						this.0 |= Self::MARK_ATTACHMENT_CLASS_FILTER.0;
						this.1 = Some(Class::parse(r)?);
					}
					Event::Group(v) => {
						// format A
						this.0 |= Self::MARK_ATTACHMENT_CLASS_FILTER.0;
						this.1 = Some(Class(vec![ClassItem::Ref(v.into())]));
					}
					e => return Err(Error::Unexpect(format!("{:?}", e))),
				},
				Event::Number(v) => {
					// format B
					this.0 = v.parse().map_err(|_| Error::Msg("lookupflag must be u16 number"))?
				}
				e => return Err(Error::Unexpect(format!("{:?}", e))),
			}
		}
		Ok(this)
	}
	#[inline]
	pub const fn contains(&self, other: &Self) -> bool {
		self.0 & other.0 == other.0
	}
}

//### 4.e. lookup
#[derive(Debug, Clone)]
pub struct LookupRef(pub String);

#[derive(Debug, Clone)]
pub struct Lookup {
	pub name: String,
	pub base: std::path::PathBuf,
	pub rules: Vec<Rule>,
	pub extension: bool,
	pub flag: LookupFlag,
}
impl Lookup {
	pub fn new(name: &str, base: std::path::PathBuf, extension: bool, flag: LookupFlag) -> Self {
		Self {
			name: name.into(),
			base,
			rules: vec![],
			extension,
			flag,
		}
	}
	pub fn parse(r: &mut Lexer<'_>, name: &str, base: std::path::PathBuf, extension: bool, flag: LookupFlag) -> Result<Self, Error> {
		let mut this = Self::new(name, base, extension, flag);
		this.parse_impl(r)?;
		r.expect(Event::Ident(name))?;
		r.expect(Event::Semi)?;
		Ok(this)
	}
	pub fn parse_impl(&mut self, r: &mut Lexer<'_>) -> Result<(), Error> {
		while let Ok(token) = r.read_next() {
			match token {
				Event::RBrace => break,
				Event::IncludeKw => {
					r.expect(Event::LParen)?;
					let path = r.read_file()?;
					r.expect(Event::RParen)?;
					r.expect(Event::Semi)?;
					let path = self.base.join(path);
					let content = std::fs::read_to_string(path).map_err(|_| Error::Msg("inlucde not found"))?;
					self.parse_impl(&mut Lexer::new(&content))?;
				}
				Event::SubKw => self.rules.push(Rule::Sub(Substitution::parse(r)?)),
				Event::PosKw => self.rules.push(Rule::Pos(Position::parse(r)?)),
				Event::RsubKw => self.rules.push(Rule::Sub(Substitution::parse_reverse(r)?)),
				Event::IgnoreKw => match r.read_next()? {
					Event::SubKw => {
						for sub in Substitution::parse_ignore(r)? {
							self.rules.push(Rule::Sub(sub));
						}
					}
					Event::PosKw => self.rules.push(Rule::Pos(Position::parse(r)?)),
					e => return Err(Error::Unexpect(format!("{:?}", e))),
				},
				Event::EnumKw => match r.read_next()? {
					Event::PosKw => self.rules.push(Rule::Pos(Position::parse(r)?)),
					e => return Err(Error::Unexpect(format!("{:?}", e))),
				},
				Event::LookupflagKw => self.flag = LookupFlag::parse(r)?,
				Event::SubtableKw => self.rules.push(Rule::SubTable),
				Event::Group(_) => {}    // TOOD?
				Event::MarkClassKw => {} // TODO?
				e => return Err(Error::Unexpect(format!("{:?}", e))),
			}
		}
		Ok(())
	}
}

#[derive(Debug, Clone)]
pub enum Rule {
	Sub(Substitution),
	Pos(Position),
	SubTable,
}

//### 4.f. markClass
#[derive(Debug, Clone)]
pub struct MarkClass {
	pub name: String,
	pub anchor: Anchor,
	pub seq: Class,
}
impl MarkClass {
	pub fn parse(r: &mut Lexer<'_>) -> Result<Self, Error> {
		match r.read_next()? {
			Event::Group(v) => {
				r.expect(Event::LAngle)?;
				let anchor = Anchor::parse(r)?;
				let name = r.expect_group()?;
				r.expect(Event::Semi)?;
				Ok(Self {
					name: name.into(),
					anchor,
					seq: Class(vec![ClassItem::Ref(v.into())]),
				})
			}
			Event::LSquare => {
				let seq = Class::parse(r)?;
				r.expect(Event::LAngle)?;
				let anchor = Anchor::parse(r)?;
				let name = r.expect_group()?;
				r.expect(Event::Semi)?;
				Ok(Self {
					name: name.into(),
					anchor,
					seq,
				})
			}
			e => Err(Error::Unexpect(format!("{:?}", e))),
		}
	}
}

// ### 4.g. subtable

#[cfg(test)]
mod tests {
	use super::*;
	#[test]
	fn language_system() {
		_ = Fea::parse("", &mut Lexer::new("languagesystem DFLT dflt;")).unwrap();
		// TODO ##### script statement:
		// TODO ##### language statement:
	}
	#[test]
	fn lookup_flag() {
		_ = LookupFlag::parse(&mut Lexer::new("IgnoreBaseGlyphs IgnoreLigatures;")).unwrap();
		_ = LookupFlag::parse(&mut Lexer::new("MarkAttachmentType @TOP_MARKS;")).unwrap();
		_ = LookupFlag::parse(&mut Lexer::new("UseMarkFilteringSet [acute grave];")).unwrap();
		_ = LookupFlag::parse(&mut Lexer::new("0;")).unwrap();
		_ = LookupFlag::parse(&mut Lexer::new("6;")).unwrap();
	}
	#[test]
	fn lookup() {
		_ = Fea::parse("", &mut Lexer::new("feature liga {lookup SHARED;} liga;")).unwrap();
		_ = Fea::parse("", &mut Lexer::new("lookup SHARED {} SHARED;")).unwrap();
		_ = Fea::parse("", &mut Lexer::new("lookup EXTENDED_KERNING useExtension {} EXTENDED_KERNING;")).unwrap();
	}
	#[test]
	fn markclass() {
		_ = Fea::parse("", &mut Lexer::new("markClass [acute grave] <anchor 350 0> @MARK_TOP_ACCENTS;")).unwrap();
		_ = Fea::parse("", &mut Lexer::new("markClass [cedilla hook] <anchor 300 0> @MARK_BOTTOM_ACCENTS;")).unwrap();
		_ = Fea::parse("", &mut Lexer::new("markClass @CLASS <anchor 400 0> @MARK_TOP_ACCENTS;")).unwrap();
	}
}
